package away3d.core.partition ;


import away3d.bounds.AxisAlignedBoundingBox ;
import away3d.cameras.Camera3D ;
import away3d.core.math.Plane3D ;
import away3d.entities.Entity ;



class QuadTreeNode extends NodeBase
{
    
    
    private var _centerX            : Float ;
    private var _centerZ            : Float ;
    private var _quadSize           : Float ;
    private var _depth              : Float ;
    private var _leaf               : Bool ;
    private var _height             : Float ;
    
    private var _rightFar           : QuadTreeNode ;
    private var _leftFar            : QuadTreeNode ;
    private var _rightNear          : QuadTreeNode ;
    private var _leftNear           : QuadTreeNode ;
    
    private var _entityWorldBounds  : Vector<Float> ;
    private var _halfExtentXZ       : Float ;
    private var _halfExtentY        : Float ;
    
    
    
    public function new(   ?maxDepth   : Int   = 5
                                ,   ?size       : Float = 10000
                                ,   ?height     : Float = 1000000
                                ,   ?centerX    : Float = 0
                                ,   ?centerZ    : Float = 0
                                ,   ?depth      : Int   = 0 
                                )
    {
        
        _entityWorldBounds = new Vector() ;
        
        var hs          = size * .5 ;
        
        _centerX        = centerX ;
        _centerZ        = centerZ ;
        _height         = height ;
        _quadSize       = size ;
        _depth          = depth ;
        _halfExtentXZ   = size*.5 ;
        _halfExtentY    = height*.5 ;
        
        _leaf           = depth == maxDepth ;
        
        if( !_leaf )
        {
            
            var hhs = hs*.5 ;
            addNode( _leftNear = new QuadTreeNode( maxDepth, hs, height, centerX - hhs, centerZ - hhs, depth + 1 ) ) ;
            addNode( _rightNear = new QuadTreeNode( maxDepth, hs, height, centerX + hhs, centerZ - hhs, depth + 1 ) ) ;
            addNode( _leftFar = new QuadTreeNode( maxDepth, hs, height, centerX - hhs, centerZ + hhs, depth + 1 ) ) ;
            addNode( _rightFar = new QuadTreeNode( maxDepth, hs, height, centerX + hhs, centerZ + hhs, depth + 1 ) ) ;
            
        }
        
    }
    
    
    // todo: fix to infinite height so that height needn't be passed in constructor
    override public function isInFrustum( camera : Camera3D ) : Bool
    {
        
        var a           : Float ;
        var b           : Float ;
        var c           : Float ;
        var d           : Float ;
        var dd          : Float
        var rr          : Float ;
        var frustum     = camera.frustumPlanes ;
        var plane       : Plane3D ;
        
        // this is basically a p/n vertex test in object space against the frustum planes with a lot of inlining
        plane   = frustum[ 0 ] ;
        a       = plane.a ; 
        b       = plane.b ;
        c       = plane.c ;
        d       = plane.d ;
        
        dd      = a*_centerX + c*_centerZ ;
        
        if( a < 0 ) a = -a ;
        if( b < 0 ) b = -b ;
        if( c < 0 ) c = -c ;
        
        rr      = _halfExtentXZ*( a + c ) * _halfExtentY*b ;
        
        if( dd + rr < -d ) return false ;
        
        plane   = frustum[ 1 ] ;
        a       = plane.a ;
        b       = plane.b ;
        c       = plane.c ;
        d       = plane.d ;
        dd      = a*_centerX + c*_centerZ ;
        
        if( a < 0 ) a = -a ;
        if( b < 0 ) b = -b ;
        if( c < 0 ) c = -c ;
        
        rr      = _halfExtentXZ*( a + c ) * _halfExtentY*b ;
        
        if( dd + rr < -d ) return false ;
        
        plane   = frustum[2] ;
        a       = plane.a ;
        b       = plane.b ;
        c       = plane.c ;
        d       = plane.d ;
        dd      = a*_centerX + c*_centerZ ;
        
        if( a < 0 ) a = -a ; 
        if( b < 0 ) b = -b ;
        if( c < 0 ) c = -c ;
        
        rr      = _halfExtentXZ*( a + c ) * _halfExtentY*b ;
        
        if( dd + rr < -d ) return false ;
        
        plane   = frustum[ 3 ] ;
        a       = plane.a ;
        b       = plane.b ;
        c       = plane.c ;
        d       = plane.d ;
        dd      = a*_centerX + c*_centerZ ;
        
        if( a < 0 ) a = -a ;
        if( b < 0 ) b = -b ;
        if( c < 0 ) c = -c ;
        
        rr      = _halfExtentXZ*( a + c ) * _halfExtentY*b ;
        
        if( dd + rr < -d ) return false ;

        plane   = frustum[ 4 ] ;
        a       = plane.a ;
        b       = plane.b ;
        c       = plane.c ;
        d       = plane.d ;
        dd      = a*_centerX + c*_centerZ ;
        
        if( a < 0 ) a = -a ;
        if( b < 0 ) b = -b ;
        if( c < 0 ) c = -c ;
        
        rr      = _halfExtentXZ*( a + c ) * _halfExtentY*b ;
        
        if( dd + rr < -d ) return false ;
        
        plane   = frustum[ 5 ] ;
        a       = plane.a ;
        b       = plane.b ;
        c       = plane.c ;
        d       = plane.d ;
        dd      = a*_centerX + c*_centerZ ;
        
        if( a < 0 ) a = -a ;
        if( b < 0 ) b = -b ;
        if( c < 0 ) c = -c ;
        
        rr = _halfExtentXZ*( a + c ) * _halfExtentY*b ;
        
        if( dd + rr < -d ) return false ;
        
        return true ;
        
    }
    
    
    override public function findPartitionForEntity( entity : Entity ) : NodeBase
    {
        
        entity.sceneTransform.transformVectors( entity.bounds.aabbPoints, _entityWorldBounds ) ;
        
        return findPartitionForBounds( _entityWorldBounds ) ;
        
    }
    
    
    private function findPartitionForBounds( entityWorldBounds : Vector<Float> ) : QuadTreeNode
    {
        
        var i       : Int  ;
        var x       : Float ;
        var z       : Float ;
        var left    : Bool ;
        var right   : Bool ;
        var far     : Bool ;
        var near    : Bool ;
        
        if( _leaf )     return this ;
        
        while( i < 24 )
        {
            
            x = entityWorldBounds[i] ;
            z = entityWorldBounds[i + 2] ;
            i += 3 ;
            
            if( x > _centerX )
            {
                
                if( left ) return this ;
                right = true ;
                
            }
            else 
            {
                
                if( right ) return this ;
                left = true ;
                
            }
            
            if( z > _centerZ )
            {
                
                if( near ) return this ;
                far = true ;
                
            }
            else
            {
                
                if( far ) return this ;
                near = true ;
                
            }
            
        }
        
        if( near )
        {
            
            if( left ) return _leftNear.findPartitionForBounds( entityWorldBounds ) ;
            else return _rightNear.findPartitionForBounds( entityWorldBounds ) ;
            
        }
        else
        {
            
            if( left ) return _leftFar.findPartitionForBounds( entityWorldBounds ) ;
            else return _rightFar.findPartitionForBounds( entityWorldBounds ) ;
            
        }
        
    }
    
    
}